﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Volo.Abp.Validation;
using YaAppoint.Appointments.Pools;
using YaAppoint.Devices;
using YaAppoint.ExamAppies;
using YaAppoint.LocalCache;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace YaAppoint.Rules
{
    /// <summary>
    /// 规则结构工厂类
    /// </summary>
    [DisableValidation]
    [DisableAuditing]
    [RemoteService(isEnabled:false)]
    public class SchemaFactory(IRepository<Device,Guid> deviceRepository,
        IRepository<RuleGroup, Guid> ruleGroupRepository,
        ILocalCacheAppService localCacheAppService,
        AsyncQueryableExecuter queryableExecuter,
        IMapper mapper
        ) : ApplicationService, ISchemaFactory, ITransientDependency
    {
        private readonly IRepository<Device, Guid> _deviceRepository = deviceRepository;
        private readonly IRepository<RuleGroup, Guid> _ruleGroupRepository = ruleGroupRepository;
        private readonly AsyncQueryableExecuter _queryableExecuter = queryableExecuter;
        private readonly IMapper _mapper = mapper;
        private readonly ILocalCacheAppService _localCacheAppService = localCacheAppService;

        /// <summary>
        /// 获取设备规则结构
        /// </summary>
        public async Task<RuleSchema> GetDeviceRuleSchema(Guid deviceId)
        {
            var schema = new RuleSchema();
            var device = await _deviceRepository.FirstOrDefaultAsync(d => d.Id == deviceId);
            if (device == null)
            {
                throw new BusinessException(CustomErrorCodes.DeviceNotExist);
            }
            schema.DeviceId = deviceId;
            schema.DepartmentId = device.DepartmentId;
            schema.DeviceTypeId = device.DeviceTypeId;

            // 全局规则结构
            var globalQuery = await _ruleGroupRepository.GetQueryableAsync();
            globalQuery = globalQuery.Where(rg => rg.Level == RuleLevel.Global
                                                                           && rg.DepartmentId == null
                                                                           && rg.DeviceTypeId == null
                                                                           && rg.DeviceId == null
                                                                           && rg.Rules.Count>0)
                                     .Include(rg => rg.Rules);

            schema.GlobalGroups = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(globalQuery));

            // 科室规则结构
            var depQuery = await _ruleGroupRepository.GetQueryableAsync();
            depQuery = depQuery.Where(rg => rg.Level == RuleLevel.Department
                                                                              && rg.DepartmentId == schema.DepartmentId
                                                                              && rg.DeviceTypeId == null
                                                                              && rg.DeviceId == null
                                                                              && rg.Rules.Count>0)
                               .Include(rg => rg.Rules);
            schema.DepartmentGroups = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(depQuery));

            // 科室设备类型规则结构
            var dTypeQuery = await _ruleGroupRepository.GetQueryableAsync();
            dTypeQuery = dTypeQuery.Where(rg => rg.Level == RuleLevel.DeviceType
                                                                              && rg.DepartmentId == schema.DepartmentId
                                                                              && rg.DeviceTypeId == schema.DeviceTypeId
                                                                              && rg.DeviceId == null
                                                                              && rg.Rules.Count>0)
                                   .Include(rg => rg.Rules);
            schema.DeviceTypeGroups = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(dTypeQuery));

            // 设备规则结构
            var deviceQuery = await _ruleGroupRepository.GetQueryableAsync();
            deviceQuery = deviceQuery.Where(rg => rg.Level == RuleLevel.Device
                                             && rg.DepartmentId == schema.DepartmentId
                                             && rg.DeviceTypeId == schema.DeviceTypeId
                                             && rg.DeviceId == schema.DeviceId
                                             && rg.Rules.Count>0)
                                    .Include(rg => rg.Rules); 
            schema.DeviceGroups = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(deviceQuery));

            return schema;
        }


        public async Task<List<RuleSchema>> GetRuleSchemaList(List<DeviceDto> deviceList)
        {
            var deviceIds = deviceList.Select(device => device.Id).ToList();
            var depId = deviceList[0].DepartmentId;
            var deviceTypeId = deviceList[0].DeviceTypeId;
            // 全局规则
            List<RuleGroupDto> globalGroupList = null;
            var globalQuery = await _ruleGroupRepository.GetQueryableAsync();
            globalQuery = globalQuery.Where(rg => rg.Level == RuleLevel.Global
                                                                           && rg.DepartmentId == null
                                                                           && rg.DeviceTypeId == null
                                                                           && rg.DeviceId ==null
                                                                           && rg.Rules.Count > 0)
                                     .Include(rg => rg.Rules);

            globalGroupList = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(globalQuery));

            // 科室规则
            List<RuleGroupDto> depGroupList = null;
            var depQuery = await _ruleGroupRepository.GetQueryableAsync();
            depQuery = depQuery.Where(rg => rg.Level == RuleLevel.Department
                                                                              && rg.DepartmentId == depId
                                                                              && rg.DeviceTypeId == null
                                                                              && rg.DeviceId == null
                                                                              && rg.Rules.Count > 0)
                               .Include(rg => rg.Rules);
            depGroupList = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(depQuery));

            // 科室设备类型规则
            List<RuleGroupDto> depDeviceTypeGroupList = null;
            var dTypeQuery = await _ruleGroupRepository.GetQueryableAsync();
            dTypeQuery = dTypeQuery.Where(rg => rg.Level == RuleLevel.DeviceType
                                                                              && rg.DepartmentId == depId
                                                                              && rg.DeviceTypeId == deviceTypeId
                                                                              && rg.DeviceId == null
                                                                              && rg.Rules.Count > 0)
                                   .Include(rg => rg.Rules);
            depDeviceTypeGroupList = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(dTypeQuery));

            // 设备规则
            List<RuleGroupDto> deviceGroupList = null;
            var deviceQuery = await _ruleGroupRepository.GetQueryableAsync();
            deviceQuery = deviceQuery.Where(rg => rg.Level == RuleLevel.Device
                                             && rg.DepartmentId == depId
                                             && rg.DeviceTypeId == deviceTypeId
                                             && deviceIds.Contains(rg.DeviceId)
                                             && rg.Rules.Count > 0)
                                    .Include(rg => rg.Rules);
            deviceGroupList = _mapper.Map<List<RuleGroup>, List<RuleGroupDto>>(await _queryableExecuter.ToListAsync(deviceQuery));


            // 在内存中组装
            List<RuleSchema> ruleSchemas = new List<RuleSchema>();
            foreach (var device in deviceList)
            { 
                var schema = new RuleSchema();
                schema.DeviceId = device.Id!.Value;
                schema.DepartmentId = depId;
                schema.DeviceTypeId = deviceTypeId;
                schema.GlobalGroups = globalGroupList;
                schema.DepartmentGroups = depGroupList;
                schema.DeviceTypeGroups = depDeviceTypeGroupList;
                schema.DeviceGroups = deviceGroupList.Where(rg => rg.DeviceId == device.Id!.Value).ToList();
                
                ruleSchemas.Add(schema);
            }
            return ruleSchemas; 
        }

        /// <summary>
        /// 生成过滤结构
        /// </summary>
        public  FilterSchema GetDeviceFilerSchema(RuleSchema ruleSchema)
        {
            var filterSchema = new FilterSchema();
            filterSchema.RequiredFilters = new List<RuleFilter>();
            filterSchema.ImproveFilters = new List<RuleFilter>();
            filterSchema.DeviceId = ruleSchema.DeviceId;

            // 提取全局规则，生成过滤结构
            var globalGroups = ruleSchema.GlobalGroups.OrderByDescending(rg => rg.Order).ToList();
            foreach (var group in globalGroups)
            {
                foreach (var rule in group.Rules)
                {
                    RuleFilter ruleFilter = null;
                    if (string.IsNullOrEmpty(rule.Script))
                    {
                        Logger.LogError($"规则名称：{rule.Name},ID:{rule.Id.ToString()},内容为空，已被从规则结构中排除");
                        continue;
                    }
                    try
                    {
                        ruleFilter =  _localCacheAppService.GetOrAddRuleFilter(rule);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex.Message);
                        continue;
                    }
                    // 添加到filterSchema
                    if (ruleFilter.LogicType == LogicType.Required)
                    {
                        filterSchema.RequiredFilters.Add(ruleFilter);
                    }
                    else
                    {
                        filterSchema.ImproveFilters.Add(ruleFilter);
                    }
                }
            }

            // 提取科室规则，生成过滤结构
            var depGroups = ruleSchema.DepartmentGroups.OrderByDescending(rg => rg.Order).ToList();
            foreach (var group in depGroups)
            {
                foreach (var rule in group.Rules)
                {
                    RuleFilter ruleFilter = null;
                    if (string.IsNullOrEmpty(rule.Script))
                    {
                        Logger.LogError($"规则名称：{rule.Name},ID:{rule.Id.ToString()},内容为空，已被从规则结构中排除");
                        continue;
                    }
                    try
                    {
                        ruleFilter =  _localCacheAppService.GetOrAddRuleFilter(rule);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex.Message);
                        continue;
                    }
                    // 添加到filterSchema
                    if (ruleFilter.LogicType == LogicType.Required)
                    {
                        filterSchema.RequiredFilters.Add(ruleFilter);
                    }
                    else
                    {
                        filterSchema.ImproveFilters.Add(ruleFilter);
                    }
                }
            }

            // 提取设备类型规则，生成过滤结构
            var deviceTypeGroups = ruleSchema.DeviceTypeGroups.OrderByDescending(rg => rg.Order).ToList();
            foreach (var group in deviceTypeGroups)
            {
                foreach (var rule in group.Rules)
                {
                    RuleFilter ruleFilter = null;

                    if (string.IsNullOrEmpty(rule.Script))
                    {
                        Logger.LogError($"规则名称：{rule.Name},ID:{rule.Id.ToString()},内容为空，已被从规则结构中排除");
                        continue;
                    }
                    try
                    {
                        ruleFilter =  _localCacheAppService.GetOrAddRuleFilter(rule);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex.Message);
                        continue;
                    }
                    // 添加到filterSchema
                    if (ruleFilter.LogicType == LogicType.Required)
                    {
                        filterSchema.RequiredFilters.Add(ruleFilter);
                    }
                    else
                    {
                        filterSchema.ImproveFilters.Add(ruleFilter);
                    }
                }
            }

            // 提取设备规则,生成过滤结构
            var deviceGroups = ruleSchema.DeviceGroups.OrderByDescending(rg => rg.Order).ToList();
            foreach (var group in deviceGroups)
            {
                foreach (var rule in group.Rules)
                {
                    RuleFilter ruleFilter = null;

                    if (string.IsNullOrEmpty(rule.Script))
                    {
                        Logger.LogError($"规则名称：{rule.Name},ID:{rule.Id.ToString()},内容为空，已被从规则结构中排除");
                        continue;
                    }
                    try
                    {
                        ruleFilter =  _localCacheAppService.GetOrAddRuleFilter(rule);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex.Message);
                        continue;
                    }
                    // 添加到filterSchema
                    if (ruleFilter.LogicType == LogicType.Required)
                    {
                        filterSchema.RequiredFilters.Add(ruleFilter);
                    }
                    else
                    {
                        filterSchema.ImproveFilters.Add(ruleFilter);
                    }
                }
            }

            return filterSchema;
        }


        /// <summary>
        /// 依据当前设备 FilerSchema，对号池进行
        /// </summary>
        /// <param name="pool">待过滤的号池</param>
        /// <param name="filterSchema">当前设备的规则过滤结构</param>
        /// <param name="priority">(输出参数) 优先级</param>
        /// <returns>号池是否符合规则要求</returns>
        /// <exception cref="NotImplementedException"></exception>
        public ResultDto ExecuteFilterSchema(PoolDto pool, List<ExamApplicationDto> applyItems, FilterSchema filterSchema)
        {
            bool isValid = true;
            int improveCount = 0;
            var requiredFilters = filterSchema.RequiredFilters;
            foreach (var filter in filterSchema.RequiredFilters)
            {
                if (!filter.Instance.ExacuteFilter(applyItems, pool))
                {
                    isValid = false;
                    break;
                }
            }
            if (isValid) // 已经满足了所有必要规则
            {
                // 优化规则的运算
                foreach (var filter in filterSchema.ImproveFilters)
                {
                    if (filter.Instance.ExacuteFilter(applyItems, pool))
                    {
                        improveCount++;
                    }
                }
            }

            return new ResultDto()
            {
                IsValid = isValid,
                SuggestOrder = improveCount
            };
        }
    }
}
